package com.mgy.db.sharding.config;

import com.mgy.db.sharding.annotation.CustomMapperScan;
import com.mgy.db.sharding.aop.MapperAspect;
import com.mgy.db.sharding.utils.ShardingYamlUtil;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.core.yaml.config.sharding.YamlRootShardingConfiguration;
import org.apache.shardingsphere.core.yaml.swapper.impl.ShardingRuleConfigurationYamlSwapper;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.shardingsphere.shardingjdbc.api.yaml.YamlShardingDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.io.InputStream;

/**
 * 基于sharding-jdbc的配置
 *
 * @author mgy
 */
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class, YamlShardingDataSourceFactory.class})
@EnableConfigurationProperties(ShardingJdbcMybatisProperties.class)
@CustomMapperScan(basePackages = {"${shardingjdbc.config.mapper-scan-base-package}"}, sqlSessionTemplateRef = "sqlSessionTemplate")
@Import(MapperAspect.class)
public class ShardingJdbcMybatisAutoConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(ShardingJdbcMybatisAutoConfiguration.class);

    @Autowired
    private ShardingJdbcMybatisProperties properties;

    @Bean("yamlShardingJdbcDataSource")
    public DataSource getYamlShardingJdbcDataSource() throws Exception {
        if (properties.getFilePath() == null || properties.getFilePath().trim().length() == 0) {
            throw new IllegalArgumentException("sharding-jdbc.config.path配置不存在");
        }
        if (!properties.getFilePath().trim().endsWith(".yaml") && !properties.getFilePath().trim().endsWith(".yml")) {
            throw new IllegalArgumentException("不是.yaml或者.yml文件");
        }

        //  File yamlFile = new PathMatchingResourcePatternResolver().getResource(properties.getFilePath()).getFile();
        ClassPathResource classPathResource = new ClassPathResource(properties.getFilePath());
        boolean isExist = false;
        try {
            isExist = classPathResource.exists();
        } catch (Exception e) {
            logger.error("文件路径不正确，file-path:" + properties.getFilePath(), e);
        }
        if (!isExist) {
            if (!properties.getFilePath().trim().startsWith("classpath:")) {
                classPathResource = new ClassPathResource("classpath:" + properties.getFilePath().trim());
            } else {
                classPathResource = new ClassPathResource(properties.getFilePath().trim().substring(10));
            }
        }
        InputStream inputStream = classPathResource.getInputStream();
        YamlRootShardingConfiguration configuration = ShardingYamlUtil.parseYaml(inputStream);
        ShardingYamlUtil.parseShardingRule(configuration);
        ShardingYamlUtil.modifyDatasource(configuration, properties.getTableMetaDataInit());
        ShardingRuleConfiguration ruleConfiguration = new ShardingRuleConfigurationYamlSwapper().swap(configuration.getShardingRule());
        return ShardingDataSourceFactory.createDataSource(configuration.getDataSources(), ruleConfiguration, configuration.getProps());
    }


    /**
     * 需要手动配置事务管理器
     * sharding-jdbc
     */
    @Bean
    @ConditionalOnMissingBean
    public DataSourceTransactionManager transactitonManager(@Qualifier("yamlShardingJdbcDataSource") DataSource yamlShardingJdbcDataSource) {
        return new DataSourceTransactionManager(yamlShardingJdbcDataSource);
    }

    /**
     * sharding-jdbc
     */
    @Bean
    @Primary
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("yamlShardingJdbcDataSource") DataSource yamlShardingJdbcDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(yamlShardingJdbcDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(properties.getMybatisMapperLocations()));
        bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(properties.getMybatisConfigLocation()));
        return bean.getObject();
    }


    @Bean
    @Primary
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
